#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
@Function:   服务端
@Author  :   Koukai
@Date    :   2019-09-28
@Version :   v1.0
@Remarks :   使用tornado框架
"""

import sys
import os
path = os.getcwd()
path = os.path.dirname(path)
sys.path.append(path)
import time
import tornado.ioloop
import tornado.web
import tornado.httpserver
from tornado.options import define, options
import json
import pdb
from collections import OrderedDict
from py2neo import Graph, Node, Relationship
import logging
from utils import log
logger_info = log.getlogger("neo4j", logging.INFO, "./log/neo4j.log")

graph = Graph('http://*.*.*.*:7474/',            # url（部署neo4j数据库的服务器地址）
              user='neo4j',       # neo4j数据库账号
              password='*********')   # neo4j数据库密码

# CQL Example
# cql_1 = MATCH (e:my_entity{name:'张伯苓'})-[r]->(p) RETURN type(r),p.name     查张伯苓的全部关系和name
# cql_2 = MATCH (e:my_entity{name:'张伯苓'})-[:`民族`]->(p) RETURN p.name     查询张伯苓的民族
# cql_3 = MATCH (e:my_entity{name:'张伯苓'})-[:`弟弟`]->(p) WHERE p.name='张彭春' RETURN p.name    查询张伯苓的弟弟为张彭春的三元组，没有则返回空
# cql_4 = MATCH (e:my_entity{name:'张伯苓'})-[r]->(p:my_entity{name:'天津'}) RETURN type(r)     查询张伯苓和天津的关系（单向）

# 设置端口
define("port", default=80, help="--port", type=int)

def get_neo4j_result_1(cql,out_dict):
    result_df = graph.run(cql).to_data_frame()
    result_dict = result_df.to_dict(orient='index')
    if result_dict == {}:
        return out_dict
    for k in result_dict.keys():
        key = result_dict[k]['type(r)']
        value = result_dict[k]['p.name']
        out_dict[key] = value
    return out_dict

def get_neo4j_result_2(cql):
    result_df = graph.run(cql).to_data_frame()
    pdb.set_trace()
    result_dict = result_df.to_dict(orient='index')
    if result_dict == {}:
        return ""
    else:
        return result_dict[0]['p.name']
    
def get_neo4j_result_3(cql):
    result_df = graph.run(cql).to_data_frame()
    result_dict = result_df.to_dict(orient='index')
    if result_dict == {}:
        return False
    else:
        return True
    
def get_neo4j_result_4(cql,out_dict):
    result_df = graph.run(cql).to_data_frame()
    result_dict = result_df.to_dict(orient='index')
    if result_dict == {}:
        return out_dict
    out_list = []
    for k in result_dict.keys():
        key = result_dict[k]['type(r)']
        out_list.append(key)
    out_dict["relation"] = out_list
    return out_dict

# tornado结构，只需要在里面添加代码，规范输出格式即可
class MainGetHandler(tornado.web.RequestHandler):
    
    def recog(self, mode="get"):
        """ 能够同时支持get和post请求 """
        if mode == "get":
            sub = self.get_argument("subject", None)
            rel = self.get_argument("relation", None)
            obj = self.get_argument("object", None)
            search_type = self.get_argument("search_type", 0)
            search_type = int(search_type)
            uid = self.get_argument("uuid", "000000")
        else:
            """ post方式接收data传递来的参数 """
            data = json.loads(self.request.body.decode())
            sub = data["subject"] if "subject" in data else None
            rel = data["relation"] if "relation" in data else None
            obj = data["object"] if "object" in data else None
            search_type = int(data["search_type"]) if "search_type" in data else 0
            uid = data["uuid"] if "uuid" in data else "000000"


        #### 配置参数 ####
        result = OrderedDict()
        returncode = 0
        message = "ok"
        output = {}
        start = time.time()

        if search_type == 0 or search_type > 4:
            returncode = 10000
            message = "search_type is error"
        
        if sub is None and rel is None and obj is None:
            returncode = 10001
            message = "data is null"
        
        if search_type == 1: # 查subject的全部relation和object，subject不能为空
            try:
                if sub == None or sub in [""," "]:
                    returncode = 10002
                    message = "when search_type is 1, subject not null"
                else:
                    cql_1 = "MATCH (e:my_entity{name:'%s'})-[r]->(p) RETURN type(r),p.name"%(sub)
                    output = get_neo4j_result_1(cql_1,output)
            except Exception as e:
                logger_info.info("{},error: {}".format(uid,e))
                returncode = 10002
                message = "service error"
        elif search_type == 2:
            try:
                if sub == None or sub in [""," "] or rel == None or rel in [""," "]:
                    returncode = 10003
                    message = "when search_type is 2, subject and rel not null"
                else:
                    cql_2 = "MATCH (e:my_entity{name:'%s'})-[:`%s`]->(p) RETURN p.name"%(sub,rel)
                    output["object"] = get_neo4j_result_2(cql_2)
            except Exception as e:
                logger_info.info("{},error: {}".format(uid,e))
                returncode = 10003
                message = "service error"
        elif search_type == 3:
            try:
                if sub == None or sub in [""," "] or rel == None or rel in [""," "] or obj == None or obj in [""," "]:
                    returncode = 10004
                    message = "when search_type is 3, subject and relation and object not null"
                else:
                    cql_3 = "MATCH (e:my_entity{name:'%s'})-[:`%s`]->(p) WHERE p.name='%s' RETURN p.name"%(sub,rel,obj)
                    output["spo_exist"] = get_neo4j_result_3(cql_3)
            except Exception as e:
                logger_info.info("{},error: {}".format(uid,e))
                returncode = 10004
                message = "service error"
        elif search_type == 4:
            try:
                if sub == None or sub in [""," "] or obj == None or obj in [""," "]:
                    returncode = 10005
                    message = "when search_type is 4, subject and object not null"
                else:
                    cql_4 = "MATCH (e:my_entity{name:'%s'})-[r]->(p:my_entity{name:'%s'}) RETURN type(r)"%(sub,obj)
                    output = get_neo4j_result_4(cql_4,output)
            except Exception as e:
                logger_info.info("{},error: {}".format(uid,e))
                returncode = 10005
                message = "service error"

        end = time.time()
        detal = end - start

        # 以json格式输出，参考输出格式
        result["returncode"] = returncode
        result["message"] = message
        result["result"] = output
        result["runtime"] = detal

        logger_info.info("result:{}".format(output))        # 本地日志
        self.write(json.dumps(result, ensure_ascii=False))  # 写结果
        self.finish()

    def get(self):
        """ get方式调用 """
        self.recog(mode="get")

    def post(self):
        """ post方式调用 """
        self.recog(mode="post")


if __name__ == "__main__":
    """ 服务器启动 """
    print("Server is listening ...")
    tornado.options.parse_command_line()
    # 域名规则，需要与nginx中配置的一致
    application = tornado.web.Application([(r"/graph_search", MainGetHandler)])
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()
